Add a blur child property to GtkOverlay
authorMatthias Clasen <mclasen@redhat.com>
Sun, 3 Sep 2017 00:42:47 +0000 (20:42 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Mon, 4 Sep 2017 18:28:16 +0000 (14:28 -0400)
When set, it blurs the content behind the child.

gtk/gtkoverlay.c

index 9c46f34130b4f4b20d016d66f576e58cb5bb1763..97ee9ebe423b70da6f52c6887b6fef0e756cbcf0 100644 (file)
@@ -25,6 +25,7 @@
 #include "gtkscrolledwindow.h"
 #include "gtkwidgetprivate.h"
 #include "gtkmarshalers.h"
+#include "gtksnapshot.h"
 
 #include "gtkprivate.h"
 #include "gtkintl.h"
@@ -65,6 +66,7 @@ struct _GtkOverlayChild
 {
   GtkWidget *widget;
   gboolean pass_through;
+  double blur;
 };
 
 enum {
@@ -76,6 +78,7 @@ enum
 {
   CHILD_PROP_0,
   CHILD_PROP_PASS_THROUGH,
+  CHILD_PROP_BLUR,
   CHILD_PROP_INDEX
 };
 
@@ -539,6 +542,17 @@ gtk_overlay_set_child_property (GtkContainer *container,
            }
        }
       break;
+    case CHILD_PROP_BLUR:
+      if (child_info)
+       {
+         if (g_value_get_double (value) != child_info->blur)
+           {
+             child_info->blur = g_value_get_double (value);
+             gtk_container_child_notify (container, child, "blur");
+              gtk_widget_queue_draw (GTK_WIDGET (overlay));
+           }
+       }
+      break;
     case CHILD_PROP_INDEX:
       if (child_info != NULL)
        gtk_overlay_reorder_overlay (GTK_OVERLAY (container),
@@ -585,6 +599,12 @@ gtk_overlay_get_child_property (GtkContainer *container,
       else
        g_value_set_boolean (value, FALSE);
       break;
+    case CHILD_PROP_BLUR:
+      if (child_info)
+       g_value_set_double (value, child_info->blur);
+      else
+       g_value_set_double (value, 0);
+      break;
     case CHILD_PROP_INDEX:
       g_value_set_int (value, g_slist_index (priv->children, child_info));
       break;
@@ -594,6 +614,77 @@ gtk_overlay_get_child_property (GtkContainer *container,
     }
 }
 
+static void
+gtk_overlay_snapshot (GtkWidget   *widget,
+                      GtkSnapshot *snapshot)
+{
+  GtkWidget *main_widget;
+  GtkWidget *child;
+  GtkAllocation main_alloc;
+  cairo_region_t *clip = NULL;
+  int i;
+
+  main_widget = gtk_bin_get_child (GTK_BIN (widget));
+  gtk_widget_get_allocation (widget, &main_alloc);
+
+  for (child = _gtk_widget_get_first_child (widget);
+       child != NULL;
+       child = _gtk_widget_get_next_sibling (child))
+    {
+      double blur;
+      gtk_container_child_get (GTK_CONTAINER (widget), child, "blur", &blur, NULL);
+      if (blur > 0)
+        {
+          GtkAllocation alloc;
+          graphene_rect_t bounds;
+
+          gtk_widget_get_allocation (child, &alloc);
+          graphene_rect_init (&bounds, alloc.x, alloc.y, alloc.width, alloc.height);
+          gtk_snapshot_push_clip (snapshot, &bounds, "Overlay Effect Clip");
+          gtk_snapshot_push_blur (snapshot, blur, "Overlay Effect");
+          gtk_widget_snapshot_child (widget, main_widget, snapshot);
+          gtk_snapshot_pop (snapshot);
+          gtk_snapshot_pop (snapshot);
+
+          if (clip == NULL)
+            {
+              clip = cairo_region_create ();
+              main_alloc.x = main_alloc.y = 0;
+              cairo_region_union_rectangle (clip, (cairo_rectangle_int_t *)&main_alloc);
+            }
+
+          cairo_region_subtract_rectangle (clip, (cairo_rectangle_int_t *)&alloc);
+        }
+    }
+
+  if (clip == NULL)
+    {
+      GTK_WIDGET_CLASS (gtk_overlay_parent_class)->snapshot (widget, snapshot);
+      return;
+    }
+
+  for (i = 0; i < cairo_region_num_rectangles (clip); i++)
+    {
+      cairo_rectangle_int_t rect;
+      graphene_rect_t bounds;
+
+      cairo_region_get_rectangle (clip, i, &rect);
+      graphene_rect_init (&bounds, rect.x, rect.y, rect.width, rect.height);
+      gtk_snapshot_push_clip (snapshot, &bounds, "Overlay Non-Effect Clip");
+      gtk_widget_snapshot_child (widget, main_widget, snapshot);
+      gtk_snapshot_pop (snapshot);
+    }
+
+  cairo_region_destroy (clip);
+
+  for (child = _gtk_widget_get_first_child (widget);
+       child != NULL;
+       child = _gtk_widget_get_next_sibling (child))
+    {
+      if (child != main_widget)
+        gtk_widget_snapshot_child (widget, child, snapshot);
+    }
+}
 
 static void
 gtk_overlay_class_init (GtkOverlayClass *klass)
@@ -603,6 +694,7 @@ gtk_overlay_class_init (GtkOverlayClass *klass)
   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
 
   widget_class->size_allocate = gtk_overlay_size_allocate;
+  widget_class->snapshot = gtk_overlay_snapshot;
 
   container_class->add = gtk_overlay_add;
   container_class->remove = gtk_overlay_remove;
@@ -624,6 +716,17 @@ gtk_overlay_class_init (GtkOverlayClass *klass)
                             FALSE,
                             GTK_PARAM_READWRITE));
 
+  /**
+   * GtkOverlay:blur:
+   *
+   * Blur the content behind this child with a Gaussian blur of this radius.
+   *
+   * Since: 3.92
+   */
+  gtk_container_class_install_child_property (container_class, CHILD_PROP_BLUR,
+      g_param_spec_double ("blur", P_("Blur Radius"), P_("Apply a blur to the content behind this child"),
+                           0, 100, 0,
+                           GTK_PARAM_READWRITE));
   /**
    * GtkOverlay:index:
    *